/**
 * 
 */
package hu.myai.gui.widgets.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * Class for storing partitions. Partitions consists of consecutive intervals.
 * 
 * @author Zsolt Safrany (u203697)
 */
public class Partition {

    private Collection<Interval> intervals;

    /**
     * Class for storing intervals. Partitions consists of consecutive
     * intervals.
     * 
     * @author Zsolt Safrany (u203697)
     */
    public static class Interval {
        /**
         * interval's inclusive start value.
         */
        private double start;
        /**
         * interval's name.
         */
        String name;
        /**
         * interval's color hint. Views may use this for their display.
         */
        int[] colorHint = new int[3];

        /**
         * Creates an interval instance.
         * 
         * @param start
         *            interval's inclusive start value
         * @param name
         *            interval's name
         */
        public Interval(double start, String name) {
            this(start, name, 0, 0, 0);
        }

        /**
         * Creates an interval instance with color hint.
         * 
         * @param start
         *            interval's inclusive start value
         * @param name
         *            interval's name
         * @param red
         *            color hint (0..255)
         * @param green
         *            color hint (0..255)
         * @param blue
         *            color hint (0..255)
         */
        public Interval(double start, String name, int red, int green, int blue) {
            setName(name);
            setStart(start);
            setColorHint(red, green, blue);
        }

        /**
         * Creates a new interval.
         * 
         * @param interval
         *            The new interval is a copy of interval.
         */
        public Interval(Interval interval) {
            this(interval.getStart(), interval.getName(), interval.getColorHint()[0], interval.getColorHint()[1],
                    interval.getColorHint()[2]);
        }

        /**
         * @return the interval's inclusive start value
         */
        public double getStart() {
            return start;
        }

        /**
         * @param start
         *            sets the interval's inclusive start value
         */
        public void setStart(double start) {
            this.start = start;
        }

        /**
         * @return the interval's name
         */
        public String getName() {
            return name;
        }

        /**
         * @param name
         *            sets the interval's name
         */
        public void setName(String name) {
            if (name == null) {
                throw new IllegalArgumentException("name must not be null");
            }
            this.name = name;
        }

        /**
         * @return colorHint array. colorHint[0] = RED, colorHint[1] = GREEN,
         *         colorHint[2] = BLUE, values are between 0 and 255.
         */
        public int[] getColorHint() {
            return colorHint;
        }

        /**
         * Set the interval's color hint.
         * 
         * @param red
         *            color hint (0..255)
         * @param green
         *            color hint (0..255)
         * @param blue
         *            color hint (0..255)
         */
        public void setColorHint(int red, int green, int blue) {
            if (red < 0 || red > 255)
                throw new IllegalArgumentException("red value is out of range (should have a value between 0 and 255)");
            if (green < 0 || green > 255)
                throw new IllegalArgumentException(
                        "green value is out of range (should have a value between 0 and 255)");
            if (blue < 0 || blue > 255)
                throw new IllegalArgumentException("blue value is out of range (should have a value between 0 and 255)");

            this.colorHint[0] = red;
            this.colorHint[1] = green;
            this.colorHint[2] = blue;
        }
    }

    /**
     * Creates a partition instance.
     * 
     * @param intervals
     *            The partition's intervals
     */
    public Partition(Collection<Interval> intervals) {
        setIntervals(intervals);
    }

    /**
     * Creates a new partition.
     * 
     * @param partition
     *            The new partition is a copy of partition.
     */
    public Partition(Partition partition) {
        ArrayList<Interval> newintervals = new ArrayList<Interval>(partition.getIntervals().size());
        for (Interval interval : partition.getIntervals()) {
            newintervals.add(new Interval(interval));
        }
        setIntervals(newintervals);
    }

    /**
     * @return the partition's intervals
     */
    public Collection<Interval> getIntervals() {
        return intervals;
    }

    /**
     * @param intervals
     *            sets the partition's intervals
     */
    public void setIntervals(Collection<Interval> intervals) {
        if (intervals == null) {
            throw new IllegalArgumentException("intervals must not be null");
        }
        if (intervals.size() == 0) {
            throw new IllegalArgumentException("no intervals are specified");
        }
        checkIntervals(intervals);
        this.intervals = intervals;
    }

    /**
     * Checks whether the intervals are consecutive or not.
     * 
     * @param intervals
     *            The intervals to be checked.
     * @throws IllegalStateException
     *             if intervals are not consecutive
     */
    private void checkIntervals(Collection<Interval> intervals) throws IllegalStateException {
        Iterator<Interval> iter = intervals.iterator();
        double lastValue = Double.NEGATIVE_INFINITY;
        while (iter.hasNext()) {
            double currentValue = iter.next().getStart();
            if (currentValue <= lastValue)
                throw new IllegalStateException("intervals are not consecutive");
            lastValue = currentValue;
        }
    }

    /**
     * @param value
     *            to be checked whether it is contained by one of the intervals
     * @return Interval that contains value
     */
    public Interval getIntervalContainingValue(double value) {
        Iterator<Interval> i = intervals.iterator();

        if (i.hasNext()) {
            Interval it = i.next();
            if (it.getStart() > value) {
                return null;
            }
            while (i.hasNext()) {
                Interval it1 = i.next();
                if (it1.getStart() > value) {
                    return it;
                } else {
                    it = it1;
                }
            }
            return it;
        } else
            return null;
    }
}
